using System;
using TFlex.Model;
using TFlex.Model.Model3D;
using TFlex.Model.Model3D.Geometry;
using TFlex.Model.Model3D.Visual;
using TFlex.Model.Model3D.Visual.Manipulators;

namespace External3D
{
	// Manipulator for dynamic editing of sphere parameters
	//
	// Манипулятор для редактирования свойств сферы
	public class SphereManipulator : BaseManipulator
	{
		// Sphere radius
		//
		// Радиус сферы
		protected double m_radius = 0.05;
		public double Radius
		{
			get
			{
				return m_radius;
			}

			set
			{
				m_radius = value;
				UpdateBoxManipulator();
			}
		}

		// Sphere center
		//
		// Центр сферы
		protected Point3D m_centre = new Point3D(0.0, 0.0, 0.0);
		public Point3D Centre
		{
			get
			{
				return new Point3D(m_centre);
			}

			set
			{
				m_centre = new Point3D(value);
				UpdateBoxManipulator();
			}
		}

		// Manipulator for configuring position of the sphere
		//
		// Манипулятор положения сферы
		protected PointManipulator m_pointManipulator = null;

		// Manipulator for configuring radius of the sphere by using its bounding cube
		//
		// Манипулятор радиуса сферы
		protected BoxManipulator m_boxManipulator = null;

		// Sphere decoration name
		//
		// Имя декорации сферы
		private readonly string m_decorationName = String.Format("SphereManipulator_{0}.Decoration",
		                                                         Guid.NewGuid().ToString("N"));

		// Constructs a manipulator
		//
		// Конструктор
		public SphereManipulator(Document document, External3DPlugin plugin)
			: base(document, plugin)
		{
		}

		// Activates this manipulator
		//
		// Запустить манипулятор
		public void Start()
		{
			if (m_pointManipulator == null)
			{
				m_pointManipulator = new PointManipulator(m_document, m_plugin);
				m_pointManipulator.Updated += OnUpdateCentre;
				m_pointManipulator.Released += OnReleaseCentre;
			}

			if (m_boxManipulator == null)
			{
				m_boxManipulator = new BoxManipulator(true, false, false);
				m_boxManipulator.OffsetEnqueued += OnUpdateBox;
				m_boxManipulator.OffsetCommited += OnCommitBox;

				UpdateBoxManipulator();
			}

			m_pointManipulator.Start();

			StartDialog();
		}

		// Deactivates this manipulator
		//
		// Остановить манипулятор
		public void Stop()
		{
			StopDialog();
			m_decorationManager.RemoveDecoration(m_decorationName);

			if (m_pointManipulator != null)
			{
				m_pointManipulator.Stop();
				m_pointManipulator = null;
			}

			if (m_boxManipulator != null)
			{
				m_boxManipulator.Dispose();
				m_boxManipulator = null;
			}
		}

		// This method is called when the user moves the sphere
		//
		// Изменение параметров манипулятора положения сферы
		private void OnUpdateCentre(IDManipulator id)
		{
			OnUpdateCentre();
			BaseUpdated(id);
		}

		// This method is called when the user changes radius of the sphere
		//
		// Изменение параметров манипулятора размеров сферы
		private void OnUpdateBox(object sender, BoxManipulator.OffsetEnqueuedEventArgs args)
		{
			double length = args.OffsetX;
			if (Math.Abs(args.OffsetY - length) >= Epsilon)
				length = args.OffsetY;
			else if (Math.Abs(args.OffsetZ - length) >= Epsilon)
				length = args.OffsetZ;

			OnUpdateBox(length);
			UpdateDialog(Math.Round(length, 2), args.Cursor);
			BaseUpdated(IDManipulator.Linear);
		}

		// This method is called when the user finishes moving the sphere
		//
		// Конец изменения параметров манипулятора положения сферы
		private void OnReleaseCentre(IDManipulator id)
		{
			OnUpdateCentre();
			BaseReleased(id);
		}

		// This method is called when the user finishes resizing the sphere
		// Конец изменения параметров манипулятора размеров сферы
		private void OnCommitBox(object sender, BoxManipulator.OffsetCommitedEventArgs args)
		{
			OnUpdateBox(args.Parameter.Value);
			StopDialog();
			BaseReleased(IDManipulator.Linear);
		}

		// Updates the sphere decoration to reflect the current state of this manipulator
		//
		// Обновляет декорацию для отображения текущего состояния манипулятора
		private void UpdateSphereDecoration()
		{
			m_decorationManager.RemoveDecoration(m_decorationName);

			DecorationContainer decorations = new DecorationContainer(m_decorationName);

			FloatVector centerVector = new FloatVector(m_centre);
			DecorationCircle circle = new DecorationCircle(String.Format("{0}.X", m_decorationName),
			                                               centerVector,
			                                               new FloatVector(1.0f, 0.0f, 0.0f),
			                                               m_radius);
			circle.Color = TFlex.Drawing.StandardColors.Decoration1;
			decorations.AddDecoration(circle);

			circle = new DecorationCircle(String.Format("{0}.Y", m_decorationName),
			                              centerVector,
			                              new FloatVector(0.0f, 1.0f, 0.0f),
			                              m_radius);
			circle.Color = TFlex.Drawing.StandardColors.Decoration1;
			decorations.AddDecoration(circle);

			circle = new DecorationCircle(String.Format("{0}.Z", m_decorationName),
			                              centerVector,
			                              new FloatVector(0.0f, 0.0f, 1.0f),
			                              m_radius);
			circle.Color = TFlex.Drawing.StandardColors.Decoration1;
			decorations.AddDecoration(circle);

			m_decorationManager.AddDecoration(decorations);
		}

		private void UpdateBoxManipulator()
		{
			if (m_boxManipulator == null)
				return;

			Direction offset = new Direction(m_radius, m_radius, m_radius);
			double diameter = m_radius * 2.0;
			m_boxManipulator.Reset(m_centre - offset, m_xAxis, m_yAxis, m_zAxis, diameter, diameter, diameter);

			UpdateSphereDecoration();
		}

		private void OnUpdateCentre()
		{
			m_centre = m_pointManipulator.Position;
			UpdateBoxManipulator();
		}

		private void OnUpdateBox(double length)
		{
			m_radius = m_converter.ToMeter(length) / 2.0;
			UpdateBoxManipulator();
		}
	}
}
